package org.jef.oss.oss.service;

import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.PutObjectResult;
import com.amazonaws.services.s3.model.S3Object;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jef.oss.core.FileProperties;
import org.jef.oss.core.FileTemplate;
import org.jef.oss.oss.client.CustomBokeOssClient;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;

/**
 * @version 1.0
 * @description: 阿里云oss
 * @author: ChengZhi 0336
 * @create: 2024-08-30 12:02
 **/
@Slf4j
@RequiredArgsConstructor
public class BokeOssTemplate implements InitializingBean, FileTemplate {

    private final FileProperties properties;

    private CustomBokeOssClient bokeOssClient;

    /**
     * 创建bucket
     * @param bucketName bucket名称
     */
    @SneakyThrows
    public void createBucket(String bucketName) {
        throw new RuntimeException("暂时不支持创建存储桶");
    }

    /**
     * 获取全部bucket
     * <p>
     *
     * API Documentation</a>
     */
    @SneakyThrows
    public List getAllBuckets() {
        throw new RuntimeException("暂时不支持该操作");
    }

    /**
     * @param bucketName bucket名称
     * @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListBuckets">AWS
     * API Documentation</a>
     */
    @SneakyThrows
    public Optional getBucket(String bucketName) {
        throw new RuntimeException("暂时不支持该操作");
    }

    /**
     * @param bucketName bucket名称
     * @see <a href=
     * "http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteBucket">AWS API
     * Documentation</a>
     */
    @SneakyThrows
    public void removeBucket(String bucketName) {
        throw new RuntimeException("暂时不支持该操作");
    }

    /**
     * 根据文件前置查询文件
     * @param bucketName bucket名称
     * @param prefix 前缀
     * @param recursive 是否递归查询
     * @return S3ObjectSummary 列表
     * @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/ListObjects">AWS
     * API Documentation</a>
     */
    @SneakyThrows
    public List getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
        throw new RuntimeException("暂时不支持该操作");
    }

    /**
     * 获取文件外链
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param expires 过期时间 <=7
     * @return url
     */
    @SneakyThrows
    public String getObjectURL(String bucketName, String objectName, Integer expires) {
        throw new RuntimeException("暂时不支持该操作");
    }

    /**
     * 获取文件
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return 二进制流
     * @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetObject">AWS
     * API Documentation</a>
     */
    @SneakyThrows
    public S3Object getObject(String bucketName, String objectName) {

        OSSObject object = bokeOssClient.getObject(objectName);
        S3Object s3Object = new S3Object();
        s3Object.setObjectContent(object.getObjectContent());

        return s3Object;
    }

    /**
     * 上传文件
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param stream 文件流
     * @throws Exception
     */
    public void putObject(String bucketName, String objectName, InputStream stream) throws Exception {
        putObject(bucketName, objectName, stream, stream.available(), "application/octet-stream");
    }

    @Override
    public void putObject(Serializable serializable, String fileName) {
        bokeOssClient.putObject(serializable, fileName);
    }

    @Override
    public <T> T getObject(String fileName, Class<T> clazz) {
        return bokeOssClient.getObject(fileName, clazz);
    }

    /**
     * 上传文件
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param stream 文件流
     * @param contextType 文件类型
     * @throws Exception
     */
    public void putObject(String bucketName, String objectName, InputStream stream, String contextType)
            throws Exception {
        putObject(bucketName, objectName, stream, stream.available(), contextType);
    }

    /**
     * 上传文件
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param stream 文件流
     * @param size 大小
     * @param contextType 类型
     * @throws Exception
     * @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/PutObject">AWS
     * API Documentation</a>
     */
    public PutObjectResult putObject(String bucketName, String objectName, InputStream stream, long size,
                                     String contextType) throws Exception {
        String filePrix = objectName.substring(0, objectName.lastIndexOf("."));
        String fileSuffix = objectName.substring(objectName.lastIndexOf("."));

        String s = bokeOssClient.uploadFileOSS(stream, filePrix, fileSuffix);
        // 图片送检执行类
        String forbiddenCheckClass = properties.getBokeoss().getForbiddenCheckClass();
        boolean b = reflectionExec(forbiddenCheckClass, objectName);
        if (!b) {
            // 抛出业务异常
            throw new RuntimeException("图片涉敏！请重新选择");
        }
        if (log.isDebugEnabled()) {
            log.debug("文件名称为：{}", s);
        }
        PutObjectResult putObjectResult = new PutObjectResult();
        return putObjectResult;
    }

    /**
     * 反射执行检测
     * @return
     */
    private boolean reflectionExec(String className, String fileName) {

        if (className == null || fileName.isEmpty()) {
            // 没配置检测类，默认不检测
            return true;
        }

        if (!fileName.endsWith(".png") && !fileName.endsWith(".jpg") && !fileName.endsWith(".jpeg")) {
            // 不是图片的不校验
            return true;
        }

        String[] s = className.split("#");
        className = s[0];
        String methodName = s[1];
        Object obj;
        Class clazz;
        Method method;
        Object returnValue;
        try {
            clazz = Class.forName(className);
            obj = clazz.newInstance();
            method = clazz.getDeclaredMethod(methodName, String.class, HttpServletRequest.class);
            String ossPath = properties.getBokeoss().getVisitpoint();
            // 组装为路径
            ossPath = ossPath + "/" + properties.getBokeoss().getImgKey() + fileName;
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = null;
            if (attributes != null) {
                request = attributes.getRequest();
            }

            returnValue = method.invoke(obj, ossPath, request);
        } catch (Exception e) {
            log.error("执行图片检测失败！", e);
            return false;
        }

        return (boolean) returnValue;
    }

    /**
     * 获取文件信息
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @see <a href="http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/GetObject">AWS
     * API Documentation</a>
     */
    public S3Object getObjectInfo(String bucketName, String objectName) throws Exception {
        throw new RuntimeException("暂时不支持该操作");
    }

    /**
     * 删除文件  现在没有申请删除权限
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @throws Exception
     * @see <a href=
     * "http://docs.aws.amazon.com/goto/WebAPI/s3-2006-03-01/DeleteObject">AWS API
     * Documentation</a>
     */
    public void removeObject(String bucketName, String objectName) throws Exception {
        bokeOssClient.deleteObject(objectName);
    }

    @Override
    public void afterPropertiesSet() {
        String accessKeyId = properties.getBokeoss().getAccessKeyId();
        String accessKeySecret = properties.getBokeoss().getAccessKeySecret();
        String bucketName = properties.getBokeoss().getBucketName();
        String imgKey = properties.getBokeoss().getImgKey();
        String endpoint = properties.getBokeoss().getEndpoint();
        this.bokeOssClient = new CustomBokeOssClient(accessKeyId, accessKeySecret, bucketName, imgKey, endpoint);
    }

}
